package cn.ztydata.teachingsystem.heaven.service;


import cn.ztydata.teachingsystem.common.Page;
import cn.ztydata.teachingsystem.exception.ServiceException;
import cn.ztydata.teachingsystem.heaven.common.WebContext;
import cn.ztydata.teachingsystem.heaven.cons.Constants;
import cn.ztydata.teachingsystem.heaven.cons.Roles;
import cn.ztydata.teachingsystem.heaven.dao.*;
import cn.ztydata.teachingsystem.heaven.entity.*;
import cn.ztydata.teachingsystem.heaven.entity.Class;
import cn.ztydata.teachingsystem.heaven.exception.ExcelException;
import cn.ztydata.teachingsystem.util.ExcelUtil;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**

 * 学生服务
 * Created by wyh on 2015/3/20.
 */
@Service
public class StudentService extends BaseService {
    @Autowired
    private StudentDao studentDao;
    @Autowired
    private DepartmentDao departmentDao;
    @Autowired
    private SpecialtyDao specialtyDao;
    @Autowired
    private DepartmentService departmentService;
    @Autowired
    private SpecialtyService specialtyService;
    @Autowired
    private StudentCourseDao studentCourseDao;
    @Autowired
    private ClassDao classDao;//班级

    @Autowired
    private WebContext webContext;
    @Autowired
    private HanyuPinyinOutputFormat hanyuPinyinOutputFormat;
    @Autowired
    private ClassGroupDao classGroupDao;



    //<editor-fold "课程群组">

    /**
     * 读取Excel文件
     * @param in 文件输入流
     *
     * @author wyh
     * @since 2015/3/20
     */
    public List<Student> readFromExcel(InputStream in) throws IOException {
        //定义标题-属性的映射
        LinkedHashMap<String, String> fieldMap = Maps.newLinkedHashMap();
        fieldMap.put("学号", "number");
        fieldMap.put("姓名", "name");
        fieldMap.put("系部编码", "deptNum");
        fieldMap.put("专业编码", "specNum");
        fieldMap.put("班级编码", "classNum");
        fieldMap.put("入学时间", "entryDate");
        fieldMap.put("出生年月", "birthday");
        fieldMap.put("性别", "sex");

        log.debug("转换Excel学生信息为List");
        return ExcelUtil.excelToList(in, Student.class, fieldMap);
    }

    /**
     * 读取Excel文件 学生异动
     * @param in 文件输入流
     *
     * @author wyh
     * @since 2015/3/20
     */
    public List<Student> readChangeFromExcel(InputStream in) throws IOException {
        //定义标题-属性的映射
        LinkedHashMap<String, String> fieldMap = Maps.newLinkedHashMap();
        fieldMap.put("学号", "number");
        fieldMap.put("系部编码", "deptNum");
        fieldMap.put("专业编码", "specNum");
        fieldMap.put("班级编码", "classNum");

        log.debug("转换Excel学生异动信息为List");
        return ExcelUtil.excelToList(in, Student.class, fieldMap);
    }

    /**
     * 读取Excel文件 学生选课
     * @param in 文件输入流
     *
     * @author wyh
     * @since 2015/3/20
     */
    public List<StudentCourse> readSCFromExcel(InputStream in) throws IOException {
        //定义标题-属性的映射
        LinkedHashMap<String, String> fieldMap = Maps.newLinkedHashMap();
        fieldMap.put("学号", "studentNum");
        fieldMap.put("课程代码", "courseCode");

        log.debug("转换Excel学生选课信息为List");
        return ExcelUtil.excelToList(in, StudentCourse.class, fieldMap);
    }

    /**
     * 检查导入学生数据中的学号冲突，并生成姓名全拼
     * @param studentList 教师集合
     *
     * @author wyh
     * @since 2015-03-20
     */
    public void checkForNum(final List<Student> studentList) {
        if (studentList.size() > Constants.TOTAL_2K) {
            log.debug("导入数据量超过" + Constants.TOTAL_2K);
            throw new ExcelException("导入数据量超过" + Constants.TOTAL_2K);
        }

        //抽取学生的学号集合
        Set<String> studentNumSet = Sets.newHashSet();
        int index = 1;
        log.debug("循环导入的学生集合");
        for (Student student : studentList) {
            if (student.getNumber() == null) {
                log.debug("学生学号 is null");

                throw new ExcelException("导入数据中第" + index + "条数据学号为空");
            }

            //检查导入数据中是否存在重复学号
            if (studentNumSet.contains(student.getNumber())) {
                log.debug("导入数据中的学生学号重复");

                throw new ExcelException("导入数据中第" + index + "条数据的学号重复" );
            }

            studentNumSet.add(student.getNumber());
            index++;

            //转换生成姓名全拼
            student.setPinyin(toPinyin(student.getName()));
        }

        //检查导入数据学号是否与数据库中记录冲突
        String duplicateNum = studentDao.getDuplicateNum(studentNumSet);
        studentNumSet.clear();
        if (duplicateNum != null) {
            log.debug("数据库中存在导入的学生学号");

            throw new ExcelException("学号为:" + duplicateNum + "的学生已经存在");
        }
    }

    /**
     * 批量新增学生
     * @param studentList 学生集合
     *
     * @author wyh
     * @since 2015-03-21
     */
    @Transactional
    public void saveStudents(final List<Student> studentList) {
        studentDao.saveStudents(studentList);
    }

    /**
     * 批量更新学生
     * @param studentList 学生集合
     *
     * @author wyh
     * @since 2015-03-21
     */
    @Transactional
    public void updateStudents(final List<Student> studentList){
        studentDao.updateStudents(studentList);
    }

    /**
     * 批量插入学生选课
     * @param scList 学生选课
     *
     * @author wyh
     * @since 2015-3-24
     */
    @Transactional
    public void saveSC(final List<StudentCourse> scList){
        try {
            studentCourseDao.saveSC(scList);
        } catch (DuplicateKeyException ex) {
            log.debug("存在重复的学生选课数据");
            String msg = ex.getMessage();
            throw new ServiceException("存在重复数据：课程代码 - 学号" + msg.substring(msg.lastIndexOf(":")));
        } catch (DataIntegrityViolationException ex) {
            log.warn("存在空或非法数据");
            throw new ServiceException("请检查数据表内的空行和非法数据");
        }
    }

    /**
     * 批量删除学生选课
     * @param scList 学生选课
     *
     * @author wyh
     * @since 2015-4-7
     */
    @Transactional
    public void deleteSC(final List<StudentCourse> scList){
        studentCourseDao.deleteSC(scList);
    }

    /**
     * 学生列表
     * @param depart 系部
     * @param major 专业
     * @param search 名字
     * @param page 页数
     * @param pageSize 条数
     * @return Page
     *
     * @author wyh
     * @since 2015-03-23
     */
    public Page<Student> listStudents(String depart, String major, String search, int page, int pageSize){
        log.debug("返回 学生列表");
        Page<Student> studentPage = studentDao.listStudents(depart, major, search, page, pageSize);

        if(studentPage.getItems().isEmpty()){
           return studentPage;
        }

        //存储学生所属班级代码集合
        List<String> classNumbers = new ArrayList<>();
        //存储系部编码集合
        List<String> deptNumbers = new ArrayList<>();
        //存储专业编码集合
        List<String> specNumbers = new ArrayList<>();

        for(Student student : studentPage.getItems()){
            classNumbers.add(student.getClassNum());

            deptNumbers.add(student.getDeptNum());

            specNumbers.add(student.getSpecNum());
        }

        //获取班级名称集合
        Map<String,String> classes = classDao.getNameByNumbers(classNumbers);
        //获取系部名称集合
        Map<String,String> departs = departmentDao.getNamesByNumbers(deptNumbers);
        //获取专业名称集合
        Map<String,String> specs = specialtyDao.getNamesByNumbers(specNumbers);

        //循环插入班级名称、系部名称和专业名称
        for(Student student : studentPage.getItems()){
            student.setClazz(classes.get(student.getClassNum()));
            student.setDepart(departs.get(student.getDeptNum()));
            student.setMajor(specs.get(student.getSpecNum()));
        }

        return studentPage;
    }

    /**
     * 学生详细
     * @param id 学生ID
     * @return Student
     *
     * @author wyh
     * @since 2015-03-23
     */
    public Student getDetail(long id){
        //获取学生信息
        Student student = studentDao.getDetail(id);

        //判断是否获取学生信息
        if(student == null){
            log.debug("学生信息为空：student == null");
            log.warn("查询记录不存在：studentId->{}", id);
            throw new ServiceException("查询记录不存在");
        }

        //获取班级信息
        Clazz clazz = classDao.getByNumber(student.getClassNum());

        //判断是否获取到指定班级信息
        if(null != clazz){
            log.debug("获取到指定班级信息:null != clazz");

            student.setClazz(clazz.getName());
        }

        log.debug("返回学生个人明细");
        return student;
    }

    /**
     * 更改学生信息
     * @param student 学生
     *
     * @author wyh
     * @since 2015-03-23
     */
    @Transactional
    public void updateStudent(Student student){
        //如果是院级用户抛出异常；
        //如果是学生但是ID不是自己的抛异常
        int role = webContext.getRole();
        if(role == Roles.COLLEGE_USER || role == Roles.STUDENT && !webContext.getUserId().equals(student.getId())){
            log.debug("没有权限：role == Roles.COLLEGE_USER || role == Roles.STUDENT && webContext.getUserId() != student.getId()");
            log.warn("非法请求：studentId->{}", student.getId());
            throw new ServiceException("非法请求");
        }
        if(studentDao.updateStudent(student) < 1){
            log.debug("更改学生信息失败：studentDao.updateStudent(student) < 1");
            log.warn("更新学生信息失败：studentId->{}", student.getId());
            throw new ServiceException("更新学生信息失败");
        }
    }

    /**
     * 更改密码
     * @param id 学生ID
     * @param pwd 密码
     *
     * @author wyh
     * @since 2015-03-23
     */
    @Transactional
    public void changePwd(long id, String pwd){
        //如果是普通教师抛出异常；
        //如果是学生但是ID不是自己的抛异常
        int role = webContext.getRole();
        if(role == Roles.TEACHER || role == Roles.STUDENT && webContext.getUserId() != id){
            log.debug("登录者没有权限：role == Roles.TEACHER || role == Roles.STUDENT && webContext.getUserId() != id");
            log.warn("非法请求：studentId->{}", id);
            throw new ServiceException("非法请求");
        }
        if(studentDao.updatePwd(id, pwd) < 1){
            log.debug("更新密码失败：studentDao.updatePwd(id, pwd) < 1");
            log.warn("更新密码失败：studentId->{}", id);
            throw new ServiceException("更新密码失败");
        }
    }

    /**
     * 检查密码
     * @param pwd 密码
     * @return boolean
     *
     * @author wyh
     * @since 2015-03-23
     */
    public boolean checkPwd(String pwd){
        //如果不是学生，报异常
        if(webContext.getRole() != Roles.STUDENT){
            log.debug("登录不是学生：webContext.getRole() != Roles.STUDENT");
            log.warn("非法请求：role->{}", webContext.getRole());
            throw new ServiceException("非法请求");
        }

        log.debug("返回 检查密码结果");
        return studentDao.checkPwd(webContext.getUserId(), pwd);
    }

    /**
     * 获取学生的姓名与头像
     * @param studentIds 教师ID集合
     * @return Map
     *
     * @author lyj
     * @since 2015-3-20
     */
    public Map<Long, Student> getPictures(final Set<Long> studentIds) {
        Map<Long, Student> result = Maps.newHashMap();
        if (studentIds == null || studentIds.isEmpty()) {
            log.debug("如果studentIds == null || studentIds.isEmpty()");
            return result;
        }

        //获取学生信息
        List<Student> Students = studentDao.getPicById(studentIds);

        //将结果转换为“教师ID->教师”的格式
        log.debug("将结果转换为“教师ID->教师”的格式");
        for (Student student : Students) {

            result.put(student.getId(), student);
        }

        log.debug("获取学生的姓名与头像");
        return result;
    }

    /**
     * 验证用户登录信息
     * @param entity
     * @return User
     *
     * @author fanruiping
     * @since 2015-04-07
     */
    public Student checkUserLogin(User entity) {
        Student student = studentDao.checkNumberAndPassword(entity);

        //验证用户登录信息是否正确
        if (null == student) {
            log.debug("未获取到指定学生信息:null == student");

            return null;
        }

        //获取系部信息
        Department department = departmentService.getDetailByNumber(student.getDeptNum());

        //判断是否获取到系部信息
        if (null != department) {
            log.debug("获取到指定系部信息:null != department");

            student.setDepart(department.getName());
        }

        //获取专业信息
        Specialty specialty = specialtyService.getDetailByNumber(student.getSpecNum());

        //判断是否获取到专业信息
        if (null != specialty) {
            log.debug("获取到指定专业信息:null != specialty");

            student.setMajor(specialty.getName());
        }

        //获取班级信息
        Clazz clazz = classDao.getByNumber(student.getClassNum());

        //判断是否获取到指定班级信息
        if(null != clazz){
            log.debug("获取到指定班级数据:null != clazz");

            student.setClassId(clazz.getId());
            student.setClazz(clazz.getName());
        }

        return student;
    }

    /**
     * 生成拼音
     * @param str 传入字符串
     * @return 拼音
     *
     * @author wyh
     * @since 2015-04-03
     */
    private String toPinyin(String str){
        char[] chars = str.toCharArray();
        StringBuilder pinyin = new StringBuilder();
        try {
            for (char c : chars) {
                String[] pinyinStr = PinyinHelper.toHanyuPinyinStringArray(c, hanyuPinyinOutputFormat);
                if (pinyinStr != null){
                    pinyin.append(pinyinStr[0]);
                }
                else {
                    pinyin.append(c);
                }
            }
        } catch (BadHanyuPinyinOutputFormatCombination ex){
            log.debug("转换姓名全拼时发生异常");
            log.warn("转换姓名全拼时发生异常:name->{}", str);
            throw new ServiceException("转换姓名全拼时发生异常:" + str);
        }
        return pinyin.toString().toUpperCase();
    }

	/**
     * 分页获取课程群组同学列表
     *
     * @param courseCode 课程代码
     * @param pageNum 当前页码
     * @param pageSize 每页条数
     * @param letter 姓名首字母
     * @param name 姓名
     * @return page
     * @author lyj
     * @since 2015-4-1
     */
    public Page<Student> getGroupList(final String courseCode, final int pageNum, final int pageSize, final String letter, final String name) {
        if (webContext.getRole() != Roles.TEACHER && webContext.getRole() != Roles.STUDENT) {
            throw new ServiceException("权限错误！");
        }
        log.debug("分页获取课程群组同学列表");
        return studentDao.getGroupList(courseCode, pageNum, pageSize, letter, name);
    }


    /**
     * 获取活跃学生列表
     *
     * @param courseCode 课程代码
     * @return JsonResultList
     * @author lyj
     * @since 2015-4-1
     */
    public List<Student> getHotStudent(final String courseCode, final int count, final long groupId) {
        if (webContext.getRole() != Roles.TEACHER && webContext.getRole() != Roles.STUDENT) {
            throw new ServiceException("权限错误！");
        }

        //根据课程代码获取所有学生信息
        List<Student> studentList = studentDao.getAllByCourseCode(courseCode);

        if (studentList.size() == 0) {
            log.debug("根据课程代码获取所有学生信息为空studentList.size() == 0");
            return studentList;
        }

        //循环获取学生活跃度
        log.debug("循环获取学生活跃度");
        for (Student student : studentList) {
            student.setActive(studentDao.getCountAll(student.getId(), groupId, courseCode));
        }

        //根据活跃度排序
        log.debug("根据活跃度排序");
        Collections.sort(studentList, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //控制升序、降序排序
                return o2.getActive().compareTo(o1.getActive());
            }
        });


        //如果获取数据条数小于集合内数据总条数
        if (count < studentList.size()) {
            log.debug("如果获取数据条数小于集合内数据总条数count < studentList.size()");
            return studentList.subList(0, count);
        } else {
            log.debug("如果获取数据条数大于等于集合内数据总条数");
            return studentList;
        }

    }

    //</editor-fold>


    //<editor-fold "班级群组">


    /**
     * 获取班级群组活跃学生列表
     *
     * @param classNum 班级代码
     * @param classId 班级ID
     * @param count
     * @return JsonResultList
     * @author lyj
     * @since 2015-5-5
     */
    public List<Student> getHotClassStudent(final String classNum, final long classId, final int count, final User userInfo) {

        //验证权限
        if (webContext.getRole() != Roles.TEACHER && webContext.getRole() != Roles.STUDENT) {
            log.debug("webContext.getRole() != Roles.TEACHER && webContext.getRole() != Roles.STUDENT，登录用户没有权限使用该接口！登录用户角色->{}, webContext.getRole()");
            log.warn("登录用户没有权限使用该接口！登录用户角色->{}", webContext.getRole());
            throw new ServiceException("权限错误！");
        }

        //判断登录用户是否可以获取该班级群组信息
        if (classGroupDao.checkGroupIdIsNum(classId, userInfo.getNumber()) == 0) {
            throw new ServiceException("权限错误！");
        }


        //根据班级代码获取所有学生信息
        List<Student> studentList = studentDao.getAllByClassCode(classNum);


        if (studentList.size() == 0) {
            log.debug("studentList.size() == 0,获取所有学生信息");
            return studentList;
        }

        //循环获取学生活跃度
        log.debug("循环获取学生活跃度");
        for (Student student : studentList) {
            student.setActive(studentDao.getClassCountAll(classId, student.getId()));
        }

        //根据活跃度排序
        Collections.sort(studentList, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //控制升序、降序排序
                return o2.getActive().compareTo(o1.getActive());
            }
        });


        //如果获取数据条数小于集合内数据总条数
        if (count < studentList.size()) {
            log.debug("如果获取数据条数小于集合内数据总条数count < studentList.size()");
            return studentList.subList(0, count);
        } else {
            log.debug("如果获取数据条数大于等于集合内数据总条数");
            return studentList;
        }

    }


    /**
     * 根据班级代码分页获取班级同学列表
     *
     * @param classNum 班级代码
     * @param letter   姓名首字母
     * @param pageNum  当前页码
     * @param pageSize 每页条数
     * @param search   关键字
     * @return page
     * @author lyj
     * @since 2015-5-6
     */
    public Page<Student> getSchoolMate(final long classId,final String classNum, final String letter, final int pageNum, final int pageSize, final String search, final User userInfo) {
        //验证权限
        if (webContext.getRole() != Roles.TEACHER && webContext.getRole() != Roles.STUDENT) {
            log.debug("webContext.getRole() != Roles.TEACHER && webContext.getRole() != Roles.STUDENT),验证权限");
            log.warn("登录用户没有权限使用该接口！登录用户角色->{}", webContext.getRole());
            throw new ServiceException("权限错误！");
        }

        //判断登录用户是否可以获取该班级群组信息
        if (classGroupDao.checkGroupIdIsNum(classId, userInfo.getNumber()) == 0) {
            throw new ServiceException("权限错误！");
        }

        log.debug("2.根据班级代码分页获取班级同学列表");
        return studentDao.getSchoolMate(classNum, letter, pageNum, pageSize, search);
    }

    //</editor-fold>
}
